package com.tbit.uqbike.webmanager.handler;

import cn.hutool.extra.servlet.ServletUtil;
import com.tbit.common.utils.IpUtil;
import com.tbit.uqbike.annos.OperateLog;
import com.tbit.common.constant.MethodEnum;
import com.tbit.uqbike.object.pojo.*;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.uqbike.service.base.GeoService;
import com.tbit.uqbike.service.base.MachineService;
import com.tbit.uqbike.service.business.*;
import com.tbit.uqbike.webmanager.controller.BaseController;
import com.tbit.uqbike.webmanager.dao.core.AccountDao;
import com.tbit.uqbike.webmanager.util.StringUtils;
import com.tbit.uqbike.webmanager.util.manager.AsyncManager;
import com.tbit.uqbike.webmanager.util.manager.factory.AsyncFactory;
import com.tbit.uqbike.webmanager.util.spring.SpringUtils;
import com.tbit.utils.ReflectUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author MyWin E-mail:335918956@qq.com
 * @version 1.0
 * @createTime 2019-12-07 17:31
 */
@Aspect
@Component
@Slf4j
public class OperateLogAspect {

    @Resource
    private AccountService accountService;
    @Resource
    private UserService userService;
    @Resource
    private MoneyOpeLogService moneyOpeLogService;
    @Resource
    private RideLogService rideLogService;
    @Resource
    private RideOrderMoneyOpeLogService rideOrderMoneyOpeLogService;


    /**
     * 对标注有operateLog注解的进行拦截,@annotation表示标注了某个注解的所有方法。
     */
    @Pointcut("@annotation(com.tbit.uqbike.annos.OperateLog)")
    public void controllerAspect() {

    }

    /**
     * 环绕
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("controllerAspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取请求参数的键和值，并记录
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //获取ip地址
        String ip = IpUtil.getClientIp(request);
        //ip地址
        final String requestURI = request.getRequestURI();
        //获取当前登录用户信息
        final LoginInfo login = BaseController.getLogin(request);
        LoginInfo loginUser = getUser(login);
        //获取目标类
        Object target = joinPoint.getTarget();
        //获取方法名称
        String methodName = joinPoint.getSignature().getName();
        Signature sign = joinPoint.getSignature();
        if (!(sign instanceof MethodSignature)) {
            throw new RuntimeException("该注解只支持方法");
        }
        MethodSignature methodSignature = (MethodSignature) sign;
        //获取方法参数类型
        Class<?>[] methodParamClazz = methodSignature.getMethod().getParameterTypes();
        String[] params = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        Map<String, Object> nameValueMap = put(methodParamClazz, params, args);
        //获取目标方法
        Method method = target.getClass().getMethod(methodName, methodParamClazz);
        Object obj = null;
        if (null != method) {
            //如果目标方法中有注解OperateLog
            if (method.isAnnotationPresent(OperateLog.class)) {
                //获取日志类
                final OperateLog annotation = method.getAnnotation(OperateLog.class);
                /**统计修改前后订单金额并保存日志*/
                /*RideLog ridelog = null;
                RideOrderMoneyOpeLog rideOrderMoneyOpeLog = null;
                if (annotation.methods() == MethodEnum.ORDER_EDIT){
                    ridelog = (RideLog) args[0];
                    rideOrderMoneyOpeLog = beforeRidelog(loginUser, annotation,  ridelog);
                }*/
                //先放行
                obj = joinPoint.proceed();
                /*异步写入日志*/
                AsyncManager.me().execute(AsyncFactory.recordOper(loginUser, annotation, nameValueMap, obj, ip, requestURI));
                /**修改用余额和礼品卡余额进行日志记录*/
                //AsyncManager.me().execute(AsyncFactory.recordUserMoneyUpdate(annotation, params, args, loginUser));
                //AsyncManager.me().execute(AsyncFactory.recordRideMoneyUpdate(annotation, rideOrderMoneyOpeLog, ridelog));
            } else {
                obj = joinPoint.proceed();
            }
        }
        return obj;
    }

    /**
     * @Description: 根据登录获取用户信息
     * @Param: [info]
     * @return: com.tbit.uqbike.object.value.LoginInfo
     * @Author: Jing
     * @Date: 2021/4/23
     */
    public LoginInfo getUser(LoginInfo info) {
        LoginInfo loginUser = new LoginInfo();
        if (StringUtils.isNotNull(info)) {
            loginUser = new LoginInfo(info.getAccountUserId(), info.getPhone(), info.getName());
            if (null != info.getAccountUserId()) {
                AccountUser accountUser = accountService.getAccountUserInfoById(info);
                Account account = SpringUtils.getBean(AccountDao.class).getByInfo(info);
                loginUser = new LoginInfo(account.getAccountId(), account.getType(), accountUser.getAccountUserId(), accountUser.getName(), accountUser.getPhone());
            }
        }
        return loginUser;
    }

    public Map<String, Object> put(Class<?>[] methodParamClazz, String[] params, Object[] args) {
        Map<String, Object> nameValueMap = new HashMap<>();
        for (int i = 0; i < methodParamClazz.length; i++) {
            //排除httpServletRequest的干扰
            if (!methodParamClazz[i].isAssignableFrom(HttpServletRequest.class)) {
                /**
                 * 如果是基本类型以及String类型
                 */
                if (ReflectUtils.typeList.contains(methodParamClazz[i].getName())) {
                    if (params != null) {
                        String param = params[i];
                        nameValueMap.put(param, args[i]);
                        put("accountId", param, "oldAccount", AccountService.class, "getById", args[i], nameValueMap);
                        put("accountUserId", param, "oldAccountUser", AccountUserService.class, "getAccountUserById", args[i], nameValueMap);
                        put("userId", param, "oldUser", UserService.class, "getByUserId", args[i], nameValueMap);
                        put("geoId", param, "oldGeo", GeoService.class, "getByGeoId", args[i], nameValueMap);
                        put("machineId", param, "oldMachine", MachineService.class, "getByMachineId", args[i], nameValueMap);
                    }
                } else {
                    Map<String, Object> fieldMap = null;
                    try {
                        fieldMap = ReflectUtils.getNameValues(args[i]);
                    } catch (Exception e) {
                        log.error(e.getMessage(), e);
                    }
                    if (StringUtils.isNotNull(fieldMap)) {
                        nameValueMap.putAll(fieldMap);
                    }
                }
            }
        }
        return nameValueMap;
    }

    /**
     * @Description: 通过反射调用方法执行
     * @Param: [key（具体参数名）, param（需要判断的参数名）, mapKey（存入map的key）, objName（实体类名）, methodName（方法名）, args（值集合）, nameValueMap（map）]
     * @return: void
     * @Author: Jing
     * @Date: 2021/4/23
     */
    public void put(String key, String param, String mapKey, Class objName, String methodName, Object args, Map<String, Object> nameValueMap) {
        Object[] objects = {args};
        if (key.equals(param)) {
            Object obj = com.tbit.uqbike.webmanager.util.reflect.ReflectUtils.invokeMethodByName(SpringUtils.getBean(objName), methodName, objects);
            if (null != obj) {
                nameValueMap.put(mapKey, obj);
            }
        }
    }

    /**
     * 修改之前订单的金额
     */
    public RideOrderMoneyOpeLog beforeRidelog(LoginInfo loginUser, OperateLog annotation, RideLog rideLog) {
        RideOrderMoneyOpeLog rideOrderMoneyOpeLog = new RideOrderMoneyOpeLog();
        if (annotation.methods() == MethodEnum.ORDER_EDIT) {
            RideLog orderNO = rideLogService.getByOrderNO(rideLog.getOrderNO());
            User user = userService.getByUserId(orderNO.getUserId());
            rideOrderMoneyOpeLog = new RideOrderMoneyOpeLog(orderNO.getAccountId(), orderNO.getOrderNO(), orderNO.getMoney(),
                    user.getUserId(), user.getPhone(), loginUser.getAccountUserId(), loginUser.getPhone()
            );
        }
        return rideOrderMoneyOpeLog;
    }


}
